package com.os.opencv.java.chapter9;

import org.opencv.core.*;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.util.ArrayList;
import java.util.List;

public class ConvexHull {

    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        //读取图像并在屏幕上显示
        Mat src = Imgcodecs.imread("pics/Shapes2.jpg", Imgcodecs.IMREAD_GRAYSCALE);

        Mat binary = new Mat();
        Imgproc.threshold(src, binary, 230, 255, Imgproc.THRESH_BINARY);

        //在屏幕上显示二值图
        HighGui.imshow("binary", binary);
        HighGui.waitKey(0);

        //根据二值图检测轮廓
        List<MatOfPoint> contour = new ArrayList<>();
        Imgproc.findContours(binary, contour, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);

        //重新获取彩色图像，用于绘制做小外接圆
        src = Imgcodecs.imread("pics/Shapes2.jpg");

        //参数准备
        MatOfInt onehull = new MatOfInt();
        List<MatOfPoint> hulls = new ArrayList<>();
        MatOfPoint c = new MatOfPoint();

        //绘制凸包
        for(int i=0; i<contour.size(); i++){
            //轮廓面积太小的跳过不画
            c = contour.get(i);
            double area = Imgproc.contourArea(c);
            if(area < 100)
                continue;
            //获取凸包，并将索引值转换为点的坐标onehull为索引值
            Imgproc.convexHull(c, onehull);
            hulls.add(indexToPoints(c, onehull));
        }
        //绘制凸包
        for(int i=0; i<hulls.size(); i++){
            Imgproc.drawContours(src, hulls, i, new Scalar(0,0,255), 2);
        }
        //在屏幕上显示绘有凸包的图像
        HighGui.imshow("convexHull", src);
        HighGui.waitKey(0);

        System.exit(0);
    }

    //将轮廓的索引值转换为点的坐标子程序
    public static MatOfPoint indexToPoints(MatOfPoint contour, MatOfInt index){
        //将两个参数转换为数组类型
        int[] ind = index.toArray();
        Point[] con = contour.toArray();

        //获取点的坐标
        Point[] pts = new Point[ind.length];
        for(int i=0; i<ind.length; i++){
            pts[i] = con[ind[i]];
        }
        //将点的坐标转换成MatOfPoint数据类型
        MatOfPoint hull = new MatOfPoint();
        hull.fromArray(pts);
        return hull;
    }
}
